package com.imooc.zuul.filters;

import com.imooc.grace.result.GraceJSONResult;
import com.imooc.grace.result.ResponseStatusEnum;
import com.imooc.utils.IPUtil;
import com.imooc.utils.JsonUtils;
import com.imooc.utils.RedisOperator;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;

/**
 * @Description : Ip 黑名单过滤器
 * @Author : YTY
 * @Date : 2020-12-10 16:00
 */

@Component
@RefreshScope       // 开启自动刷新
public class BlackIpFilter extends ZuulFilter {

    @Value("${blackIp.continueCounts}")
    private Integer continueCounts;
    @Value("${blackIp.timeInterval}")
    private Integer timeInterval;
    @Value("${blackIp.limitTimes}")
    private Integer limitTimes;

    @Autowired
    private RedisOperator redis;

    @Override
    public String filterType() {

        return "pre";
    }

    @Override
    public int filterOrder() {

        return 2;
    }

    @Override
    public boolean shouldFilter() {

        return true;
    }

    /**
     * 过滤器的业务实现
     */
    @Override
    public Object run() throws ZuulException {

        System.out.println("执行【Ip黑名单】过滤器...");

        System.out.println("continueCounts: " + continueCounts);
        System.out.println("timeInterval: " + timeInterval);
        System.out.println("limitTimes: " + limitTimes);

        // 获得上下文对象
        RequestContext context = RequestContext.getCurrentContext();
        HttpServletRequest request = context.getRequest();

        // 获得Ip
        String ip = IPUtil.getRequestIp(request);

        /**
         * 判断Ip在10秒内的请求次数是否超过10次
         * 如果超过，则限制这个Ip访问15秒，15秒后放行
         */

        final String ipRedisKey = "zuul-ip:" + ip;
        final String ipRedisLimitKey = "zuul-ip-limit:" + ip;

        // 获得当前ip这个key的剩余时间
        long limitLeftTime = redis.ttl(ipRedisLimitKey);
        // 如果当前限制ip的key还存在剩余时间，则不能访问，继续等待
        if (limitLeftTime > 0) {
            stopRequest(context);
            return null;
        }

        // 在Redis累加ip的请求访问次数
        long requestCounts = redis.increment(ipRedisKey, 1);
        // 从0开始计算请求次数，初期访问为1，则设置过期时间，连续请求间隔时间
        if (requestCounts == 1) {
            redis.expire(ipRedisKey, timeInterval);
        }

        // 如果还能取得请求次数，说明用户连续请求的次数在10秒内
        // 一旦请求次数超过连续访问次数，则限制ip访问
        if (requestCounts > continueCounts) {
            // 限制ip访问时间
            redis.set(ipRedisLimitKey, ipRedisLimitKey, limitTimes);
            stopRequest(context);
        }

        return null;
    }

    private void stopRequest(RequestContext context) {

        // 停止 Zuul 继续路由，禁止请求通信
        context.setSendZuulResponse(false);
        context.setResponseStatusCode(200);
        String result = JsonUtils.objectToJson(
                GraceJSONResult.errorCustom(
                        ResponseStatusEnum.SYSTEM_ERROR_ZUUL));
        context.setResponseBody(result);
        context.getResponse().setCharacterEncoding("utf-8");
        context.getResponse().setContentType(MediaType.APPLICATION_JSON_VALUE);
    }

}
